﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using TR0217.ControlEx.Properties;

namespace TR0217.ControlEx
{
    public class CustomBorderForm : Form
    {
        #region Properties
        #region Border

        public new FormBorderStyle FormBorderStyle
        {
            get { return base.FormBorderStyle; }
            set { return; }
        }

        private Color _outlineColor = Color.DarkGray;
        public System.Drawing.Color OutlineColor
        {
            get { return _outlineColor; }
            set
            {
                _outlineColor = value;
                this.Invalidate();
            }
        }

        private Color _borderColor = SystemColors.Control;
        public System.Drawing.Color BorderColor
        {
            get { return _borderColor; }
            set 
            { 
                _borderColor = value;
                this.Invalidate();
            }
        }
        private int _radius = 9;
        public int Radius
        {
            get { return _radius; }
            set 
            {
                _radius = value;
                this.Invalidate();
            }
        }

        public new Padding Padding
        {
            get { return base.Padding; }
            set
            {
                if (_hasCaption)
                    value.Top = value.Top < 23 ? 23 : value.Top;
                base.Padding = value;
                this.Refresh();
            }
        }

        private bool _resizable = true;
        public bool Resizable
        {
            get { return _resizable; }
            set { _resizable = value; }
        }

        #endregion

        #region Caption
        private bool _hasCaption = false;
        public bool HasCaption
        {
            get { return _hasCaption; }
            set 
            {
                if (!_collapsible || value)
                {
                    bool b = _hasCaption;
                    _hasCaption = value;
                    if (_hasCaption && !b)
                        this.Padding = new Padding(Padding.Left, Padding.Top + 23, Padding.Right, Padding.Bottom);
                    else if (!_hasCaption && b)
                        this.Padding = new Padding(Padding.Left, Padding.Top - 23, Padding.Right, Padding.Bottom);
                    this.Invalidate();
                }
            }
        }

        private Color _captionBeginColor = SystemColors.ActiveCaption;
        private Color _captionEndColor = SystemColors.GradientActiveCaption;
        private LinearGradientMode _captionGradientMode = LinearGradientMode.Vertical;
        [Description("Get the caption gradient's start color."), Category("Caption"), Browsable(false)]
        public Color CaptionBeginColor
        {
            get { return _captionBeginColor; }
            set
            {
                _captionBeginColor = value;
                this.Invalidate();
            }
        }

        [Description("Get the caption gradient's end color."), Category("Caption"), Browsable(false)]
        public Color CaptionEndColor
        {
            get { return _captionEndColor; }
            set
            {
                _captionEndColor = value;
                this.Invalidate();
            }
        }

        [Description("Change the caption gradient direction."), Category("Caption"), Browsable(true)]
        public LinearGradientMode CaptionGradientMode
        {
            get { return _captionGradientMode; }
            set
            {
                _captionGradientMode = value;
                this.Invalidate();
            }
        }
        #endregion 

        #region Collapsible
        private bool _collapsed;
        private bool _autoCollapse = false;
        private bool _collapsible = false;

        public bool Collapsible
        {
            get { return _collapsible; }
            set 
            { 
                _collapsible = value;
                if (_collapsible)
                {
                    _resizable = false;
                    HasCaption = true;
                    ControlBox = true;
                }
                this.Refresh();
            }
        }

        public EventHandler CollapsedChanged;

        /// <summary>
        /// Indicates whether the component should collapse it self when mouse move out of is area and expend whem mouse move in if it was collapsed.
        /// Cannot be changed at run time.
        /// </summary>
        [Bindable(true)]
        [Category("Collapse")]
        [Description("Indicates whether the component should collapse it self when mouse move out of is area and expend when mouse move in if it was collapsed. Cannot be changed at run time.")]
        public bool AutoCollapse
        {
            get { return _autoCollapse; }
        }

        /// <summary>
        /// Indicates whether the component is collapsed.
        /// </summary>
        [Browsable(false)]
        public bool Collapsed
        {
            get { return _collapsed; }
            set
            {
                if (_collapsed != value)
                {
                    this.Collapse(value);
                    if (this.CollapsedChanged != null)
                        this.CollapsedChanged(this, new EventArgs());
                }
            }
        }
        #endregion

        #endregion

        public CustomBorderForm()
        {
            base.FormBorderStyle = FormBorderStyle.None;
            base.Padding = new System.Windows.Forms.Padding(3);
            SetStyle(
                ControlStyles.UserPaint |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.ResizeRedraw, true);
            this.UpdateStyles();
        }

        private bool _collapsing = false;
        private int _originalHeight;
        /// <summary>
        /// Collapse or expend the pane
        /// </summary>
        /// <param name="_collapsed">true for collapse, false for expend</param>
        public void Collapse(bool collapsed)
        {
            if (_collapsible)
            {
                if (collapsed && !_collapsed)
                {
                    _originalHeight = Height;
                    _collapsing = true;
                    this.Height = (Padding.Top + Padding.Bottom - 1);
                    _collapsed = true;
                    _collapsing = false;
                }
                else if (!collapsed && _collapsed)
                {
                    this.Height = _originalHeight;
                    _collapsed = false;
                }
            }
        }

        #region overides

        private Rectangle _rectCaption;
        private Rectangle _rectIcon;
        private Rectangle _rectText;
        private int _controlBoxCount = 0;
        private int _rectHelpButton = -1;
        private int _rectCollapseButton = -1;
        private int _rectMinimizeButton = -1;
        private int _rectSizeButton = -1;
        private int _rectCloseButton = -1;
        private Rectangle[] _rectControlBox = new Rectangle[5];
        private Bitmap[] _bmpControl = new Bitmap[5];
        private int _mousePosition = -1;

        private void CalcCaptionRects()
        {
            _rectCaption.X = Padding.Left;
            _rectCaption.Y = Padding.Top-24;
            _rectCaption.Width = Width - Padding.Left - Padding.Right;
            _rectCaption.Height = 23;

            _rectIcon.X = _rectCaption.X+2;
            _rectIcon.Y = _rectCaption.Y + 2;
            _rectIcon.Width = 20;
            _rectIcon.Height = 20;


            _rectText.X = this.ShowIcon ? _rectIcon.X + 23: _rectIcon.X;
            _rectText.Y = _rectIcon.Y+3;
            _rectText.Height = 20;
            _rectText.Width = _rectCaption.Width - 22;

            int x = _rectCaption.Right - 21;
            int y = _rectCaption.Top+4;
            for (int i = 0; i < 5; i++)
            {
                _rectControlBox[i].X = x;
                _rectControlBox[i].Y = y;
                _rectControlBox[i].Width = 16;
                _rectControlBox[i].Height = 16;
                x -= 18;
            }

            _controlBoxCount = 0;
            if (ControlBox)
            {
                _rectCloseButton = _controlBoxCount;
                _bmpControl[_controlBoxCount] = Resources.close;
                if (_resizable)
                {
                    _rectSizeButton = ++_controlBoxCount;
                    _bmpControl[_controlBoxCount] = Resources.maximize;
                    if (WindowState == FormWindowState.Maximized)
                        _bmpControl[_controlBoxCount] = Resources.restore;
                }

                if (ShowInTaskbar&&_resizable)
                {
                    _rectMinimizeButton = ++_controlBoxCount;
                    _bmpControl[_controlBoxCount] = Resources.minimize;
                }
                if (_collapsible)
                {
                    _rectCollapseButton = ++_controlBoxCount;
                    _bmpControl[_controlBoxCount] = Resources.collapse;
                    if (_autoCollapse) _bmpControl[_controlBoxCount] = Resources.collapsible;
                }
                if (HelpButton)
                {
                    _rectHelpButton = ++_controlBoxCount;
                    _bmpControl[_controlBoxCount] = Resources.help;
                }
                _rectText.Width = _rectControlBox[_controlBoxCount].Left - _rectIcon.Right;
                _controlBoxCount = ++_controlBoxCount;
            }
        }

        private void DrawCaption(Graphics g)
        {
            LinearGradientBrush gradientBrush = new LinearGradientBrush(_rectCaption, _captionBeginColor, _captionEndColor, _captionGradientMode);
            g.FillRectangle(gradientBrush, _rectCaption);
            gradientBrush.Dispose();
            if (this.ShowIcon)
                g.DrawIcon(this.Icon, _rectIcon);
  
            StringFormat format = new StringFormat();
            format.FormatFlags = StringFormatFlags.NoWrap;
            format.LineAlignment = StringAlignment.Center;
            format.Alignment = StringAlignment.Near;
            format.Trimming = StringTrimming.EllipsisCharacter;
            
            g.DrawString(this.Text, SystemFonts.CaptionFont, SystemBrushes.ActiveCaptionText, _rectText);

            format.Dispose();
            for (int i = 0; i < _controlBoxCount; i++)
            {
                if (_mousePosition == i)
                {
                    Color c = _lbPressedPosition == i ? _captionBeginColor : _captionEndColor;
                    //Brush b = new LinearGradientBrush(_rectCaption, _captionEndColor,_captionBeginColor, _captionGradientMode);
                    Brush b = new SolidBrush(c);
                    g.FillRectangle(b, _rectControlBox[i]);
                    b.Dispose();
                }
                g.DrawImageUnscaled(_bmpControl[i], _rectControlBox[i]);
            }
        }

        protected override void OnCreateControl()
        {           
            base.OnCreateControl();
            this.CalcCaptionRects();
            _mouseIn = this.Bounds.Contains(Control.MousePosition);
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if(_hasCaption)
                DrawCaption(e.Graphics);

            SolidBrush fillBrush = new SolidBrush(_borderColor);
            GraphicsPath path = new GraphicsPath();
            path.AddRectangle(new Rectangle(0, 0, Width, Height));
            int top = _hasCaption ? Padding.Top - 23 : Padding.Top;

            if (_radius != 0)
            {
                path.AddArc(Padding.Left-1, top-1, _radius * 2, _radius * 2, 180, 90);
                path.AddLine(Padding.Left + _radius, top, Width - Padding.Right - _radius, top);
                path.AddArc(Width - Padding.Right - _radius * 2, top-1, _radius * 2, _radius * 2, 270, 90);
                path.AddLines(new Point[] {
                new Point(Width - Padding.Right,top+_radius),
                new Point(Width - Padding.Right,Height-Padding.Bottom),
                new Point(Padding.Left,Height-Padding.Bottom),
                new Point(Padding.Left,top+_radius-1)});
            }
            else
                path.AddRectangle(new Rectangle(Padding.Left,top,Width-Padding.Left-Padding.Right,Height-Padding.Bottom-top));

            e.Graphics.FillPath(fillBrush, path);
            fillBrush.Dispose();
            path.Dispose();

            Graphics g = e.Graphics;
            Pen p = new Pen(_outlineColor, 2);
            p.Alignment = PenAlignment.Center;
            if (_radius != 0)
            {
                g.DrawArc(p, -1, -1, _radius * 2, _radius * 2, 180, 90);
                g.DrawLine(p, _radius, 0, Width - _radius, 0);
                g.DrawArc(p, Width - _radius * 2, -1, _radius * 2, _radius * 2, 270, 90);
                g.DrawLines(p, new Point[] {
                new Point(Width,_radius-1),
                new Point(Width,Height),
                new Point(0,Height),
                new Point(0,_radius)});
            }
            else
                g.DrawRectangle(p, 0, 0, Width, Height);
            p.Dispose();
        }


        private Rectangle _rectLeft;
        private Rectangle _rectRight;
        private Rectangle _rectBottom;
        private Rectangle _rectLeftBottom;
        private Rectangle _rectRightBottom;

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (this.Height < (Padding.Top+ Padding.Bottom-1))
            {
                this.Height = (Padding.Top + Padding.Bottom-1);
            }

            if (this.Width < (114+Padding.Left+Padding.Right))
            {
                this.Width = (114+Padding.Left+Padding.Right);
            }
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (_radius != 0)
            {
                GraphicsPath shape = new GraphicsPath();
                shape.AddArc(-1, -1, _radius * 2, _radius * 2, 180, 90);
                shape.AddLine(_radius, 0, Width - _radius, 0);
                shape.AddArc(Width - _radius * 2, -1, _radius * 2, _radius * 2, 270, 90);
                shape.AddLines(new Point[] {
                new Point(Width,_radius),
                new Point(Width,Height),
                new Point(0,Height),
                new Point(0,_radius)});
                shape.Flatten();
                Region r = this.Region;
                this.Region = new Region(shape);
                shape.Dispose();
                if (r != null) r.Dispose();
            }
            else
            {
                Region r = this.Region;
                this.Region = null;
                if (r != null) r.Dispose();
            }

            _rectLeft = new Rectangle(0, 0, 5, this.Height - 5);
            _rectRight = new Rectangle(this.Width - 5, 0, 5, this.Height - 5);
            _rectBottom = new Rectangle(5, this.Height - 5, this.Width - 5, 5);
            _rectLeftBottom = new Rectangle(0, this.Height - 5, 5, 5);
            _rectRightBottom = new Rectangle(this.Width - 5, this.Height - 5, 5, 5);
            this.CalcCaptionRects();
        }
        private const int HTNOWHERE = 0;
		private const int HTCLIENT = 1;
		private const int HTSYSMENU = 3;
        private const int HTMINBUTTON = 8;
		private const int HTMAXBUTTON = 9;
		private const int HTCLOSE = 20;
		private const int HTHELP = 21;
        private const int HTCAPTION = 2;
        private const int HTLEFT = 10;
        private const int HTRIGHT = 11;
        private const int HTBOTTOM = 15;
        private const int HTBOTTOMLEFT = 16;
        private const int HTBOTTOMRIGHT = 17;
        private const int WM_NCHITTEST = 0x84;
        private const int WM_NCLBUTTONDBLCLK = 0xA3;
        private const int WM_NCMOUSEMOVE = 0xA0;
        private const int WM_NCLBUTTONDOWN = 0xA1;
        private const int WM_NCLBUTTONUP = 0xA2;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_MOUSEMOVE = 0x200;
        private const int HTTOP = 12;
        private const int HTBORDER = 18;
		private const int HTOBJECT = 19;
        private const int HTMENU = 5;
        private const int HTHSCROLL = 6;

        private bool _mouseIn = false;

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if (_autoCollapse)
            {
                if (!this.Bounds.Contains(Control.MousePosition))
                {
                    _mouseIn = false;
                    this.Collapsed = true;
                }
            }
        }

        protected int _lbPressedPosition = -1;
        protected override void WndProc(ref Message m)
        {
            Point pts = this.PointToClient(Control.MousePosition);
            if (!_resizable && m.Msg == WM_NCLBUTTONDBLCLK)
                return;
            base.WndProc(ref m);
            if (m.Msg == WM_NCMOUSEMOVE || m.Msg == WM_MOUSEMOVE)
            {
                if (_autoCollapse)
                {
                    if (!_mouseIn && !_collapsing)
                    {
                        _mouseIn = true;
                        this.Collapsed = false;
                    }
                }

                if (m.Msg == WM_NCMOUSEMOVE)
                {
                    _mousePosition = -1;
                    for (int i = 0; i < _controlBoxCount; i++)
                    {
                        if (_rectControlBox[i].Contains(pts))
                        {
                            _mousePosition = i;
                            break;
                        }
                    }
                    if (_lbPressedPosition != _mousePosition) _lbPressedPosition = -1;
                    Invalidate();
                }
            }
            else if (m.Msg == WM_NCLBUTTONDOWN)
            {
                _lbPressedPosition = -1;
                for (int i = 0; i < _controlBoxCount; i++)
                {
                    if (_rectControlBox[i].Contains(pts))
                    {
                        _lbPressedPosition = i;
                        break;
                    }
                }
                Invalidate();
            }
            else if (m.Msg == WM_NCLBUTTONUP)
            {
                for (int i = 0; i < _controlBoxCount; i++)
                {
                    if (_rectControlBox[i].Contains(pts))
                    {
                        if (_lbPressedPosition == i)
                        {
                            this.ProcessControlBoxClick(i);
                            _lbPressedPosition = -1;
                        }
                        break;
                    }
                }
            }
            else if (m.Msg == WM_NCHITTEST)
            {
                if (_resizable)
                {
                    if (_rectLeftBottom.Contains(pts))
                    {
                        m.Result = new IntPtr(HTBOTTOMLEFT);
                        return;
                    }
                    else if (_rectRightBottom.Contains(pts))
                    {
                        m.Result = new IntPtr(HTBOTTOMRIGHT);
                        return;
                    }
                    else if (_rectLeft.Contains(pts))
                    {
                        m.Result = new IntPtr(HTLEFT);
                        return;
                    }
                    else if (_rectRight.Contains(pts))
                    {
                        m.Result = new IntPtr(HTRIGHT);
                        return;
                    }
                    else if (_rectBottom.Contains(pts))
                    {
                        m.Result = new IntPtr(HTBOTTOM);
                        return;
                    }
                }

                if (_rectText.Contains(pts))
                    m.Result = new IntPtr(HTCAPTION);
                else if (_rectIcon.Contains(pts))
                    m.Result = new IntPtr(HTSYSMENU);
                else if (_rectCaption.Contains(pts))
                    m.Result = new IntPtr(HTOBJECT);
                else
                    m.Result = new IntPtr(HTCLIENT);

            }
        }

        private void ProcessControlBoxClick(int i)
        {
            if (_rectHelpButton == i)
            {
                base.OnHelpButtonClicked(new CancelEventArgs());
            }
            else if (_rectCollapseButton == i)
            {
                _autoCollapse = !_autoCollapse;
                _bmpControl[_rectCollapseButton] = Resources.collapse;
                if (_autoCollapse) _bmpControl[_rectCollapseButton] = Resources.collapsible;
                Invalidate();
            }
            else if (_rectMinimizeButton == i)
            {
                WindowState = FormWindowState.Minimized;
            }
            else if (_rectSizeButton == i)
            {
                WindowState=WindowState == FormWindowState.Maximized ? FormWindowState.Normal : FormWindowState.Maximized;
            }
            else
            {
                Close();
            }
        }
        #endregion

        #region other

        //private static bool isSameColor(Color color1, Color color2)
        //{
        //    if (color1.A != color2.A)
        //    {
        //        return false;
        //    }

        //    if (color1.B != color2.B)
        //    {
        //        return false;
        //    }

        //    if (color1.G != color2.G)
        //    {
        //        return false;
        //    }

        //    if (color1.R != color2.R)
        //    {
        //        return false;
        //    }

        //    return true;
        //}

        //private void PaintForm(Graphics graphics)
        //{
        //    Bitmap _bmp = new Bitmap(this.Width, this.Height);
        //    Graphics g = Graphics.FromImage(_bmp);
        //    GraphicsPath shape = new GraphicsPath();

        //    g.SmoothingMode = SmoothingMode.HighSpeed;
        //    g.CompositingQuality = CompositingQuality.HighQuality;
        //    g.InterpolationMode = InterpolationMode.High;

        //    SolidBrush bSolid = new SolidBrush(Color.Pink);
        //    g.FillRectangle(bSolid, 0, 0, Width, _radius * 2);
        //    bSolid.Dispose();

        //    shape.AddArc(0, 0, _radius * 2, _radius * 2, 180, 90);
        //    shape.AddLine(_radius, 0, Width - _radius, 0);
        //    shape.AddArc(Width - _radius * 2 - 1, 0, _radius * 2, _radius * 2, 270, 90);
        //    shape.AddLines(new Point[] {
        //        new Point(Width,_radius),
        //        new Point(Width,Height),
        //        new Point(0,Height),
        //        new Point(0,_radius)});
        //    shape.Flatten();

        //    SolidBrush bBack = new SolidBrush(this.BackColor);
        //    g.FillPath(bBack, shape);
        //    bBack.Dispose();

        //    shape.Reset();
        //    shape.AddArc(0, 0, _radius * 2, _radius * 2, 180, 90);
        //    shape.AddLine(_radius, 0, Width - _radius, 0);
        //    shape.AddArc(Width - _radius * 2 - 1, 0, _radius * 2, _radius * 2, 270, 90);
        //    shape.AddLines(new Point[] {
        //        new Point(Width-1,_radius),
        //        new Point(Width-1,Height-1),
        //        new Point(0,Height-1),
        //        new Point(0,_radius)});
        //    shape.Flatten();

        //    Pen p = new Pen(_outlineColor, 1);
        //    p.Alignment = PenAlignment.Inset;
        //    g.DrawPath(p, shape);
        //    p.Dispose();

        //    shape.Reset();

        //    // Loop through every pixel in the top left corner.
        //    // Create a 1 x 1 rectangle regions of pixels that do not match the transparent color
        //    for (int x = 0; x < _radius; x++)
        //    {
        //        for (int y = 0; y < _radius; y++)
        //        {
        //            if (isSameColor(_bmp.GetPixel(x, y), Color.Pink) == false)
        //            {
        //                shape.AddRectangle(new Rectangle(x, y, 1, 1));
        //            }
        //        }
        //    }

        //    // Loop through every pixel in the top right corner.
        //    // Create a 1 x 1 rectangle regions of pixels that do not match the transparent color
        //    for (int x = Width - _radius; x < Width; x++)
        //    {
        //        for (int y = 0; y < _radius; y++)
        //        {
        //            if (isSameColor(_bmp.GetPixel(x, y), Color.Pink) == false)
        //            {
        //                shape.AddRectangle(new Rectangle(x, y, 1, 1));
        //            }
        //        }
        //    }

        //    shape.AddRectangle(new Rectangle(_radius, 0, Width - _radius * 2, _radius));
        //    shape.AddLines(new Point[] {
        //        new Point(Width,_radius),
        //        new Point(Width,Height),
        //        new Point(0,Height),
        //        new Point(0,_radius)});

        //    this.Region = new Region(shape);

        //    shape.Dispose();

        //    graphics.DrawImageUnscaled(_bmp, 0, 0, this.Width, this.Height);
        //    _bmp.Dispose();
        //}

        #endregion
    }
}
